package reward

import (
	"math/rand"
	"sync"

	"gddgame.cc/galaxy/common/dsl/game"
	"gddgame.cc/galaxy/common/dsl/game/item"
	"gddgame.cc/galaxy/utils"
)

//go:generate msgp

type GiftType string

//msgp:tuple GiftNumber
type GiftNumber struct {
	Key    string `json:"key" msg:"key"`
	Number int    `json:"number" msg:"number"`
}

//msgp:tuple ItemNumber
type ItemNumber struct {
	Id     int    `json:"id" msg:"id"`
	Number int    `json:"number" msg:"number"`
	Name   string `json:"name" msg:"name"`
	Icon   string `json:"icon" msg:"icon"`
	Desc   string `json:"desc" msg:"desc"`
}

//msgp:tuple GoldReward
type GoldReward struct {
	Gold int `json:"gold" msg:"gold"`
}

func GoldRewardFromReward(reward *Reward) *GoldReward {
	r := &GoldReward{Gold: reward.Gold}
	return r
}

//msgp:tuple Reward
type Reward struct {
	Gold    int          `json:"gold" msg:"gold"`
	Diamond int          `json:"diamond" msg:"diamond"`
	Gifts   []GiftNumber `json:"gifts" msg:"gifts"`
	Items   []ItemNumber `json:"items" msg:"items"`

	items map[int]int
	gifts map[string]int
}

func RewardFromGoldReward(reward *GoldReward) *Reward {
	r := &Reward{Gold: reward.Gold}
	return r
}

func (reward *Reward) SetItems(items map[int]int) {
	reward.Items = make([]ItemNumber, 0, len(items))
	for key, value := range items {
		item := ItemNumber{
			Id:     key,
			Number: value,
		}
		reward.Items = append(reward.Items, item)
	}
	reward.items = items
}

func (reward *Reward) BindInfo(itemMap map[int]*item.Item, lang string) {
	if reward.Items == nil {
		return
	}
	for key := range reward.Items {
		item := itemMap[reward.Items[key].Id]
		reward.Items[key].Name = item.Get(lang, "name")
		reward.Items[key].Icon = item.Icon
		reward.Items[key].Desc = item.Get(lang, "desc")
	}
}

func (reward *Reward) GetItems() map[int]int {
	if reward.Items == nil {
		return map[int]int{}
	}
	if reward.items == nil {
		reward.items = make(map[int]int, len(reward.Items))
		for _, item := range reward.Items {
			reward.items[item.Id] = item.Number
		}
	}
	return reward.items
}

func (reward *Reward) SetGifts(gifts map[string]int) {
	reward.Gifts = make([]GiftNumber, 0, len(gifts))
	for key, value := range gifts {
		reward.Gifts = append(reward.Gifts, GiftNumber{
			Key:    key,
			Number: value,
		})
	}
	reward.gifts = gifts
}

func (reward *Reward) GetGifts() map[string]int {
	if reward.Gifts == nil {
		return map[string]int{}
	}
	if reward.gifts == nil {
		reward.gifts = make(map[string]int, len(reward.Gifts))
		for _, gift := range reward.Gifts {
			reward.gifts[gift.Key] = gift.Number
		}
	}
	return reward.gifts
}

func (reward *Reward) MergeReward(r *Reward) {
	if r == nil {
		return
	}
	reward.Gold += r.Gold
	reward.Diamond += r.Diamond
	items := r.GetItems()
	gifts := r.GetGifts()
	oldItems := reward.GetItems()
	oldGifts := reward.GetGifts()
	for key, value := range items {
		if _, ok := oldItems[key]; ok {
			oldItems[key] += value
		} else {
			oldItems[key] = value
		}
	}
	reward.SetItems(oldItems)
	for key, value := range gifts {
		if _, ok := oldGifts[key]; ok {
			oldGifts[key] += value
		} else {
			oldGifts[key] = value
		}
	}
	reward.SetGifts(oldGifts)
}

func (reward *Reward) Copy() Reward {
	r := Reward{
		Gold:    reward.Gold,
		Diamond: reward.Diamond,
	}
	if reward.Gifts != nil {
		r.Gifts = make([]GiftNumber, len(reward.Gifts))
		for index, value := range reward.Gifts {
			r.Gifts[index] = GiftNumber{
				Key:    value.Key,
				Number: value.Number,
			}
		}
	}
	if reward.Items != nil {
		r.Items = make([]ItemNumber, len(reward.Items))
		for index, value := range reward.Items {
			r.Items[index] = ItemNumber{
				Id:     value.Id,
				Number: value.Number,
			}
		}
	}
	return r
}

var (
	rewardLogicMap = sync.Map{}
)

type RewardType byte

const (
	RewardGold         RewardType = 1
	RewardItem                    = 2
	RewardGoldItem                = 3
	RewardGift                    = 4
	RewardGoldGift                = 5
	RewardItemGift                = 6
	RewardGoldItemGift            = 7
)

type RewardLogic struct {
	GoldMax    int32 `json:"gold_max"`
	GoldMin    int32 `json:"gold_min"`
	ItemNumber int32 `json:"item_number"`
	ConsumeMax int32 `json:"consume_max"`
	ItemMax    int32 `json:"item_max"`
	ItemMin    int32 `json:"item_min"`
	GiftStep   int32 `json:"gift_step"`

	weightRandom *utils.WeightRandom
	init         bool
}

func GenerateLogic(env *game.Environment, percent int, maxItem int, rewardType RewardType) *RewardLogic {
	p := int32(percent)
	if p < 10 {
		p = 10
	}
	if p > 100 {
		p = 100
	}
	logic := &RewardLogic{
		GoldMin:    0,
		GoldMax:    0,
		ItemNumber: 0,
		ConsumeMax: 0,
		ItemMax:    0,
		ItemMin:    0,
		GiftStep:   0,
		init:       false,
	}
	if rewardType&RewardGold > 0 {
		logic.GoldMin = rand.Int31n(p)
		logic.GoldMax = rand.Int31n(p*10-logic.GoldMin) + logic.GoldMin
	}
	if rewardType&RewardItem > 0 {
		logic.ItemNumber = rand.Int31n(3) + 1
		//  保证道具质量按percent提高而提高，不用2次随机
		logic.ItemMax = p * int32(maxItem) / 50
		logic.ItemMin = logic.ItemMax / 2
		logic.ConsumeMax = int32(maxItem) - logic.ItemMin
	}

	if rewardType&RewardGift > 0 {
		var RewardGiftLogic [][]map[GiftType]int
		if err := env.Config(RewardGiftLogicConfig, &RewardGiftLogic); err == nil {
			logic.GiftStep = 100 / p / int32(len(RewardGiftLogic))
		}
	}

	rewardLogicMap.Store(percent, logic)
	//fmt.Print(logic, maxItem)
	return logic
}

func (rl *RewardLogic) AddItems(items map[int]*item.SettingItem) {
	if rl.init {
		return
	}
	rl.weightRandom = utils.NewWeightRandom()
	for key := range items {
		item := items[key]
		if item.Level >= int(rl.ItemMin) && item.Level <= int(rl.ItemMax) {
			if item.IsConsume {
				rl.weightRandom.Add(item.Id, item.Level)
			} else {
				rl.weightRandom.Add(item.Id, item.Level*10)
			}
		}
	}
	rl.init = true
}

func (rl *RewardLogic) GetItem() int {
	return rl.weightRandom.Random().(int)
}
